package com.vr.xuecheng.search.service.impl;

import cn.hutool.core.util.StrUtil;
import com.vr.xuecheng.base.exception.XueChengPlusException;
import com.vr.xuecheng.search.dto.SearchParams;
import com.vr.xuecheng.search.model.CourseIndex;
import com.vr.xuecheng.search.respository.CourseIndexRespository;
import com.vr.xuecheng.search.service.ICourseIndexService;
import com.vr.xuecheng.search.vo.SearchCoursePageVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.SearchHitsImpl;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

/**
 * @author hzh
 * @date 2025/03/22
 */
@Service
@Log4j2
@RequiredArgsConstructor
public class CourseIndexService implements ICourseIndexService {
    private final CourseIndexRespository courseIndexResponsitory;
    private final ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Override
    public boolean addCourseIndex(CourseIndex courseIndex) {
        if (courseIndex == null) {
            log.error("添加课程索引不能为空");
            XueChengPlusException.cast("添加课程索引不能为空");
        }
        CourseIndex save = courseIndexResponsitory.save(courseIndex);
        return save.getId() != null;
    }

    @Override
    public SearchCoursePageVO searchCoursePage(SearchParams searchParams) {
        SearchHits<CourseIndex> searchHits = searchCourse(searchParams);
        ArrayList<String> mtList = new ArrayList<>();
        ArrayList<String> stList = new ArrayList<>();
        ArrayList<CourseIndex> items = new ArrayList<>();
        for (SearchHit<CourseIndex> searchHit : searchHits) {
            if (searchHit == null) {
                continue;
            }
            items.add(searchHit.getContent());
            if (searchHit.getContent().getMtName() != null) {
                mtList.add(searchHit.getContent().getMtName());
            }
            if (searchHit.getContent().getStName() != null) {
                stList.add(searchHit.getContent().getStName());
            }
        }
        // 构建结果
        SearchCoursePageVO searchCoursePageVO = new SearchCoursePageVO();
        searchCoursePageVO.setCounts(searchHits.getTotalHits());
        searchCoursePageVO.setMtList(mtList);
        searchCoursePageVO.setStList(stList);
        searchCoursePageVO.setItems(items);

        return searchCoursePageVO;
    }

    private SearchHits<CourseIndex> searchCourse(SearchParams searchParams) {
        if (searchParams == null) {
            log.error("搜索条件为空");
            XueChengPlusException.cast("搜索条件为空");
        }
        if (searchParams.getPageNo() == null || searchParams.getPageSize() == null) {
            log.error("分页参数不能为空");
            XueChengPlusException.cast("分页参数不能为空");
        }
        // 开始搜索
        // 用bool查询组合多个查询条件
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 搜索关键字非空时，构建多字段匹配查询
        if (StrUtil.isNotBlank(searchParams.getKeywords())) {
            // 搜索关键字
            MultiMatchQueryBuilder multiMatchQuery = QueryBuilders.multiMatchQuery(searchParams.getKeywords())
                    // 优先按照名字查询，权重提高三倍
                    .field("name")
                    .field("companyName ")
                    .field("description")
                    .field("tags")
                    .type(MultiMatchQueryBuilder.Type.BEST_FIELDS);
            // 最佳字段匹配
            // 添加到 must 子句（影响相关性评分）
            boolQuery.must(multiMatchQuery);
        } else {
            boolQuery.must(QueryBuilders.matchAllQuery());
        }
        // 分类过滤：精确匹配分类 ID（mt、st、grade 均为 keyword 类型）
        if (StrUtil.isNotBlank(searchParams.getMt())) {
            // 不参加分词，并且不计分数
            boolQuery.filter(QueryBuilders.termQuery("mtName", searchParams.getMt()));
        }
        if (StrUtil.isNotBlank(searchParams.getSt())) {
            // 不参加分词，并且不计分数
            boolQuery.filter(QueryBuilders.termQuery("stName", searchParams.getSt()));
        }
        if (StrUtil.isNotBlank(searchParams.getGrade())) {
            // 不参加分词，并且不计分数
            boolQuery.filter(QueryBuilders.termQuery("grade", searchParams.getGrade()));
        }
        // 高亮构造器
        HighlightBuilder highlightBuilder = new HighlightBuilder()
                .field("name")
                .field("description")
                .field("tags")
                .preTags("<em class='eslight'>")
                .postTags("</em>")
                .requireFieldMatch(false);
        // 排序
        // 构建 Native 查询，并设置分页参数
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder()
                .withHighlightBuilder(highlightBuilder)
                .withQuery(boolQuery)
                .withPageable(PageRequest.of(Math.toIntExact(searchParams.getPageNo() - 1), Math.toIntExact(searchParams.getPageSize())));
        if (searchParams.getSortType() != null) {
            Sort sort = Sort.by(Sort.Order.desc("price"));
            switch (searchParams.getSortType()) {
                case 1:
                    // 按价格升序
                    sort = Sort.by(Sort.Order.asc("price"));
                    break;
                case 2:
                    // 按价格降序
                    sort = Sort.by(Sort.Order.desc("price"));
                    break;
            }
            queryBuilder.withSort(sort);
        }
        SearchHits<CourseIndex> searchHits = elasticsearchRestTemplate.search(queryBuilder.build(), CourseIndex.class);
        // 添加高亮

        return processHighlight(searchHits);
    }

    private SearchHits<CourseIndex> processHighlight(SearchHits<CourseIndex> searchHits) {
        List<SearchHit<CourseIndex>> searchHitList = searchHits.getSearchHits().parallelStream().map(hit -> {
            CourseIndex courseIndex = hit.getContent();
            Map<String, List<String>> highlightFields = hit.getHighlightFields();
            applyHighlight(highlightFields, "name", courseIndex::setName);
            applyHighlight(highlightFields, "description", courseIndex::setDescription);
            applyHighlight(highlightFields, "tags", courseIndex::setTags);
            return hit;
        }).toList();
        return new SearchHitsImpl<>(
                searchHits.getTotalHits(),
                searchHits.getTotalHitsRelation(),
                searchHits.getMaxScore(),
                null,
                searchHitList,
                searchHits.getAggregations(),
                searchHits.getSuggest()
        );
    }

    private void applyHighlight(Map<String, List<String>> highlightFields, String fileName, Consumer<String> setter) {
        // 查看字段需不需要添加高亮
        if (highlightFields.containsKey(fileName)) {
            List<String> fragments  = highlightFields.get(fileName);
            if (!fragments.isEmpty()) {
                // 相当于调用了setter方法
                setter.accept(String.join("", fragments));
            }
        }
    }
}
